home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 9 / develop 9 code / NeoTextBox / NeoTextBox.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-01-06  |  11.7 KB  |  329 lines  |  [TEXT/MPS ]

  1. /********************************************************************************
  2.  
  3. NeoTextBox.c - a replacement for TextBox
  4.  
  5. Written by Bryan K. Ressler (Beaker)
  6.  
  7. ********************************************************************************/
  8. #include "NTBDemo.h"
  9. #include "NeoTextBox.h"
  10.  
  11. /** STATIC PROTOTYPES **********************************************************/
  12. static unsigned short NTBLineHeight(unsigned char *theText, unsigned long textLen,
  13.     Rect *wrapBox, short lhCode, short *startY);
  14. static void NTBDraw(StyledLineBreakCode breakCode, unsigned char *lineStart,
  15.     long lineBytes, Rect *wrapBox, short align, short curY, short boxWidth);
  16.  
  17. /********************************************************************************
  18. *
  19. * NTBLineHeight - figures line height
  20. *
  21. * Input:    theText        the entire text that was given to the NeoTextBox call
  22. *            textLen        the length in bytes of the text
  23. *            lhCode        the line height code that was passed to NeoTextBox
  24. *            startY        VAR - we return the starting vertical pen location here
  25. *
  26. * Output:    returns the line height to use
  27. *            
  28. ********************************************************************************/
  29. unsigned short NTBLineHeight(unsigned char *theText, unsigned long textLen,
  30.     Rect *wrapBox, short lhCode, short *startY)
  31. {
  32.     short            asc,desc;    /* Used in the OutlineMetrics calls */
  33.     FontInfo        fInfo;        /* The old-style font information record */
  34.     Point            frac;        /* The fraction for the TrueType calls */
  35.     unsigned short    lineHeight;    /* The return value */
  36.  
  37.     GetFontInfo(&fInfo);
  38.     if (lhCode < 0) {
  39.  
  40.         /*
  41.             If the user has specified variable-height lines, we need to try
  42.             to determine the tallest ascent in the given text.  We can only
  43.             really do this if the font is a TrueType font.  Otherwise, we
  44.             punt and use old-fashioned GetFontInfo numbers.
  45.         */
  46.  
  47.         frac.h = frac.v = 1;
  48.         if (gHasTrueType && IsOutline(frac, frac)) {
  49.  
  50.             /*
  51.                 At this point we know the current font is a TrueType font, so
  52.                 we do an OutlineMetrics call with our full text.  It will put
  53.                 the tallest character ascent into asc, and the deepest descent
  54.                 into desc.  Then we choose between whichever's most between
  55.                 the old-style ascent/descent and the numbers we get from
  56.                 the OutlineMetrics call.
  57.             */
  58.  
  59.             OutlineMetrics((short)textLen, theText, frac, frac, &asc, &desc,
  60.                 nil, nil, nil);
  61.             lineHeight = MAXOF(fInfo.ascent, asc) + MAXOF(fInfo. descent,-desc) +
  62.                 fInfo.leading;
  63.             *startY = wrapBox->top + MAXOF(fInfo.ascent, asc);
  64.             *startY += fInfo.leading;
  65.  
  66.         } else {
  67.         
  68.             /*
  69.                 At this point we know the current font isn't TrueType, so we
  70.                 just use the old way of calculating line height.
  71.             */
  72.             
  73.             lineHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  74.             *startY = wrapBox->top + fInfo.ascent + fInfo.leading;
  75.         }
  76.  
  77.     } else if (lhCode == 0) {
  78.  
  79.         /*
  80.             If the user has specified "default" line height, he just wants us
  81.             to get the line height from the FontInfo record.
  82.         */
  83.         
  84.         lineHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  85.         *startY = wrapBox->top + fInfo.ascent + fInfo.leading;
  86.         
  87.     } else {
  88.         
  89.         /* If the user has provided a specific line height, we just trust
  90.             them.  We can't really generate too good of a starting vertical
  91.             coordinate, but we munge one together anyway.
  92.         */
  93.  
  94.         lineHeight = lhCode;
  95.         *startY = wrapBox->top + lhCode + fInfo.leading;
  96.  
  97.     }
  98.  
  99.     return(lineHeight);
  100. }
  101.  
  102. /********************************************************************************
  103. *
  104. * NTBDraw - draws a line with appropriate justification
  105. *
  106. * Input:    breakCode    the break code that was returned from StyledLineBreak
  107. *            lineStart    pointer to the beginning of the text for the current line
  108. *            lineBytes    the length in bytes of the the text for this line
  109. *            wrapBox        the box within which we're wrapping
  110. *            align        the text alignment as specified by the user
  111. *            curY        our current vertical pen coordinate
  112. *            boxWidth    the width of wrapBox (since NeoTextBox already calculated it)
  113. *
  114. * Output:    none (draws on the screen)
  115. *            
  116. ********************************************************************************/
  117. void NTBDraw(StyledLineBreakCode breakCode, unsigned char *lineStart,
  118.     long lineBytes, Rect *wrapBox, short align, short curY, short boxWidth)
  119. {
  120.     unsigned long    blackLen;    /* Length of non-white characters */
  121.     short            slop;        /* Number of pixels of slop for full just */
  122.  
  123.     /*
  124.         The first thing we do here is determine the length of the "black" part
  125.         of the current line.  This excludes spaces, carriage returns, and other
  126.         white stuff depending on the language.  How do we know what to elim-
  127.         inate?  We DON'T!  So we ask our friend the Script Manager to do it
  128.         for us.  VisibleLength returns the number of bytes that we should use
  129.         for pixel width calculations.
  130.     */
  131.     
  132.     blackLen = VisibleLength(lineStart, lineBytes);
  133.     
  134.     if (align == ntbJustFull) {
  135.     
  136.         /*
  137.             For full justification, we need to calculate the "slop" space on
  138.             the line that's not filled up by the text.  Then we move to the
  139.             margin and get ready to draw BUT WAIT!  If this is the last line of
  140.             a paragraph, we need to draw it with whatever the system justifi-
  141.             cation is.  So if the break code indicates we're at the end of the
  142.             text, or we can find a carriage return there ourselves, we just
  143.             change the text alignment to the current default system text align-
  144.             ment and fall through without drawing.  If it's just another line
  145.             within a paragraph, we use the handy Script Manager routine DrawJust
  146.             to draw it justified.  In languages like Arabic, full justification
  147.             is performed differently than just spacing out the words.  DrawJust
  148.             handles cases like these correctly.
  149.             
  150.             Note that when we go looking for the carriage return at the end of
  151.             the line, it's okay to simply index to the last byte of the string.
  152.             This is because the carriage return character, 0x0d, is in the
  153.             control-code range, which is defined to never be the low byte of a
  154.             two byte character.
  155.         */
  156.  
  157.         slop = boxWidth - TextWidth(lineStart, 0, blackLen);
  158.         MoveTo(wrapBox->left, curY);
  159.         if (breakCode == smBreakOverflow ||
  160.             *(lineStart + (lineBytes - 1)) == kReturnChar)
  161.             align = GetSysJust();
  162.         else DrawJust(lineStart, blackLen, slop);
  163.     }
  164.  
  165.     /*
  166.         For the rest of the text alignments (left, center, and right), we just
  167.         move the pen to the right place and draw the text with DrawText.  Note
  168.         that the alignments that could have come into the NeoTextBox call have
  169.         been resoved down to one of the four constants teForceLeft, teJustRight
  170.         teJustCenter, or ntbJustFull.
  171.     */
  172.     
  173.     switch(align) {
  174.         case teForceLeft:
  175.         case teJustLeft:
  176.             MoveTo(wrapBox->left, curY);
  177.             break;
  178.         case teJustRight:
  179.             MoveTo(wrapBox->right - TextWidth(lineStart, 0, blackLen), curY);
  180.             break;
  181.         case teJustCenter:
  182.             MoveTo(wrapBox->left + (boxWidth - TextWidth(lineStart, 0, blackLen)) / 2,
  183.                 curY);
  184.             break;
  185.     }
  186.     if (align != ntbJustFull)
  187.         DrawText(lineStart, 0, lineBytes);
  188. }
  189.  
  190. /********************************************************************************
  191. *
  192. * NeoTextBox - word-wraps text inside a given box
  193. *
  194. * Input:    theText        the text we need to wrap
  195. *            textLen        the length in bytes of the text
  196. *            wrapBox        the box within which we're wrapping
  197. *            align        the text alignment
  198. *                            teForceLeft, teFlushLeft    left justified
  199. *                            teJustCenter, teCenter        center justified
  200. *                            teJustRight, teFlushRight    right justified
  201. *                            ntbJustFull                    full justified
  202. *                            teJustLeft, teFlushDefault    system justified
  203. *            lhCode        the line height code that was passed to NeoTextBox
  204. *                            < 0        variable - based on tallest character
  205. *                            0        default - based on GetFontInfo
  206. *                            > 0        fixed - use lhCode as the line height
  207. *            endY        VAR - if non-nil, the vertical coord of the last line
  208. *            lhUsed        VAR - if non-nil, the line height used to draw the text
  209. *
  210. * Output:    returns the number of line drawn total (even if they drew outside of
  211. *            the boundries of wrapBox)
  212. *            
  213. ********************************************************************************/
  214. short NeoTextBox(unsigned char *theText, unsigned long textLen, Rect *wrapBox,
  215.     short align, short lhCode, short *endY, short *lhUsed)
  216. {
  217.     RgnHandle            oldClip;        /* Saved clipping region */
  218.     StyledLineBreakCode    breakCode;        /* Returned code from StyledLineBreak */
  219.     Fixed                fixedMax;        /* boxWidth converted to fixed point */
  220.     Fixed                wrapWid;        /* Width to wrap to */
  221.     short                boxWidth;        /* Width of the wrapBox */
  222.     long                lineBytes;        /* Number of bytes in one line */
  223.     unsigned short        lineHeight;        /* Calculated line height */
  224.     short                curY;            /* Current vertical pen location */
  225.     unsigned short        lineCount;        /* Number of lines we've drawn */
  226.     long                textLeft;        /* Pointer to remaining bytes of text */
  227.     unsigned char        *lineStart;        /* Pointer to beginning of a line */
  228.     unsigned char        *textEnd;        /* Pointer to the end of input text */
  229.  
  230.     /*
  231.         First, we save the old clipping region and clip to wrapBox.  Then, figure
  232.         the width of wrapBox, and make a fixed point version of it.  Also, resolve
  233.         the text alignment teFlushDefault to be the default system text alignment.
  234.     */
  235.  
  236.     GetClip((oldClip = NewRgn()));
  237.     ClipRect(wrapBox);
  238.     boxWidth = wrapBox->right - wrapBox->left;
  239.     fixedMax = Long2Fix((long)boxWidth);
  240.     if (align == teFlushDefault)
  241.         align = GetSysJust();
  242.     
  243.     /*
  244.         Now we call NTBLineHeight to calculate the appropriate line height.  It
  245.         also figures our starting vertical pen location in curY based on the
  246.         line height and other metric parameters.  Clear lineCount, set
  247.         lineStart to point to the beginning of the text, calculate textEnd for
  248.         comparison to know when we're done, and preset textLeft to be all the
  249.         text.
  250.     */
  251.  
  252.     lineHeight = NTBLineHeight(theText, textLen, wrapBox, lhCode, &curY);
  253.     lineCount = 0;
  254.     lineStart = theText;
  255.     textEnd = theText + textLen;
  256.     textLeft = textLen;
  257.     
  258.     /*
  259.         This is the main wrap-and-draw loop.  I bet you never thought wrapping
  260.         text could be so easy...
  261.     */
  262.  
  263.     do {
  264.         
  265.         /*
  266.             Every line, we have to preset lineBytes to something non-zero.
  267.             This tells StyledLineBreak that we're drawing the first format
  268.             run on the line (of course, for us, there's only ONE format run
  269.             total).  Also preset wrapWid.  StyledLineBreak will always modify
  270.             lineBytes (to tell you how many bytes are on this line), and will
  271.             modify wrapWid, so we have to reset them each line.
  272.         */
  273.         
  274.         lineBytes = 1;
  275.         wrapWid = fixedMax;
  276.  
  277.         breakCode = StyledLineBreak(lineStart, textLeft, 0, textLeft, 0,
  278.             &wrapWid, &lineBytes);
  279.         
  280.         /*
  281.             Now that the Script Manager has done all the really hard work for
  282.             us, we draw the line.  We already knew lineStart, StyledLineBreak
  283.             gave us lineBytes, which we pass to NTBDraw.  It'll handle the
  284.             different text alignments itself.
  285.         */
  286.         
  287.         NTBDraw(breakCode, lineStart, lineBytes, wrapBox, align, curY, boxWidth);
  288.         
  289.         /*
  290.             Now we advance our vertical position down by the height of one
  291.             line, advance lineStart by the number of bytes we just drew,
  292.             calculate a new textLeft, and increment our line count.
  293.         */
  294.         
  295.         curY += lineHeight;
  296.         lineStart += lineBytes;
  297.         textLeft -= lineBytes;
  298.         lineCount++;
  299.         
  300.     } while (lineStart < textEnd);
  301.     
  302.     /*
  303.         Well that was a job well done.  Let's return some useful values, too.
  304.         If the user gave pointers for endY and lhUsed, we stuff our ending
  305.         vertical coordinate and the line height we calculated into those,
  306.         respectively.  These allow the guy to put something after the wrapped
  307.         text (or at least know the right place to put it).
  308.     */
  309.  
  310.     if (endY)
  311.         *endY = curY - lineHeight;
  312.     if (lhUsed)
  313.         *lhUsed = lineHeight;
  314.     
  315.     /*
  316.         Finally, restore the clipping region, dispose of the region, and
  317.         return the TOTAL number of lines drawn (note that we didn't stop
  318.         drawing when curY advanced past wrapBox->bottom.  This way, the user
  319.         could tell that the text overflowed wrapBox.  If you want to know how
  320.         many lines fit, divide wrapBox by lhUsed.  This way, you get the best
  321.         of both worlds.
  322.     */
  323.  
  324.     SetClip(oldClip);
  325.     DisposeRgn(oldClip);
  326.  
  327.     return(lineCount);
  328. }
  329.